using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using Sio.Mdm.Utils;

namespace Sio.Mdm.Server
{
    static class ServiceScanner
    {
        #region Methods

        #region Public

        #region Scan
        /// <summary>
        /// Scan for possible services on passed path 
        /// </summary>
        /// <param name="path">Path to serach for services</param>
        /// <param name="recursive">Scan subdirectories</param>
        public static void Scan(string path, bool recursive)
        {
            if (String.IsNullOrEmpty(path) || path == "true")
            {
                path = AppDomain.CurrentDomain.BaseDirectory;
            }

            if (!Directory.Exists(path))
            {
                throw new Exception(String.Format("Scanning path {0} is invalid!", path));
            }

            string []files = Directory.GetFiles(path, "*.dll", recursive? SearchOption.AllDirectories: SearchOption.TopDirectoryOnly);
            Dictionary<string, bool> foundServices = new Dictionary<string, bool>();

            if (files.Length > 0)
            {
                Resolver.AddProbingPath(path);

                foreach (string filePath in files)
                {
                    try
                    {  
                        Resolver.AddProbingPath(filePath);

                        Assembly asm = Assembly.LoadFrom(filePath);
                        Type[] types = asm.GetExportedTypes();

                        foreach (Type type in types)
                        {
                            ServiceAttribute attr = GetServiceAttribute(type);

                            if (attr != null)
                            {   
                                ServiceInformation service = ServiceInformation.FromType(type);

                                service.Name = attr.Name;
                                
                                // Probably base class
                                if (String.Compare(service.Name, "ServiceStub") == 0)
                                {
                                    continue;
                                }

                                service.DefaultInterface = String.Format("{0}, {1}", attr.DefaultInterface.FullName, attr.DefaultInterface.Assembly.GetName().Name);

                                if (!foundServices.ContainsKey(service.Name))
                                {
                                    Config.AddServiceInternal(service);
                                    foundServices.Add(service.Name, true);

                                    if (ServiceFound != null)
                                    {
                                        ServiceFound(service);
                                    }
                                }
                                else
                                {
                                    Logger.WriteWarning("Duplicate service name ({0}). Only first one will be available.", service.Name);
                                }
                            }
                        }
                    }
                    catch (ReflectionTypeLoadException ex)
                    {
                        Logger.WriteError("Could not load {0}", filePath);
                        Logger.WriteException(ex);

                        foreach (Exception inner in ex.LoaderExceptions)
                        {
                            Logger.WriteException(inner);
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.WriteError("Could not load {0}", filePath);
                        Logger.WriteException(ex);
                    }
                }
            }
        }
        #endregion

        #region GetServiceAttribute
        private static ServiceAttribute GetServiceAttribute(Type type)
        {
            object[] attributes = type.GetCustomAttributes(true);

            foreach (object attr in attributes)
            {
                if (attr.GetType().Name.Contains("ServiceAttribute"))
                {
                    return attr as ServiceAttribute;
                }
                if (attr is ServiceAttribute)
                {
                    return attr as ServiceAttribute;
                }
            }

            return null;
        }
        #endregion

        #endregion

        #endregion

        #region Events

        #region Public

        #region ServiceFound
        public delegate void OnServiceFound(ServiceInformation service);
        public static event OnServiceFound ServiceFound; 
        #endregion

        #endregion

        #endregion
    }
}
